home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK1.toast / Development Kits (Disc 1) / Macintosh Drag and Drop / Demo Applications / FinderDrag / FinderDrag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-09  |  30.8 KB  |  1,193 lines  |  [TEXT/MPS ]

  1.  
  2. #include "FinderDrag.h"     // nothing interesting is in FinderDrag.h
  3. #include "StringLib.h"
  4. #include "Spaces.h"
  5. #include "FinderStuff.h"
  6. #include "MoreDesktopMgr.h"
  7.  
  8. // menu constants
  9. #define kAppleMenuID 1
  10. #define kAboutMenuItem 1
  11. #define kFileMenuID 2
  12. #define kNewMenuItem 1
  13. #define kOpenMenuItem 2
  14. #define kCloseMenuItem 3
  15. #define kQuitMenuItem 5
  16.  
  17.  
  18.  
  19. // general global variables
  20.  
  21. Boolean            gQuitFlag;    // true when the app should exit the main event loop
  22.  
  23. Boolean            gHasColorQuickdrawFlag; // Gestalt info
  24.  
  25. MenuHandle        gAppleMenuHandle, gFileMenuHandle;
  26.  
  27. unsigned short    gWindowCounter;
  28.  
  29. DragHandlerGlobals gDragHandlerGlobals;
  30.  
  31.  
  32. /***************************************************
  33.  *    Drag support routines
  34.  *
  35.  *    These are the installed drag handler routines
  36.  *    and supporting functions
  37.  ***************************************************/
  38.  
  39. // —————————————————————————————————————————————————————————————————————————
  40. // —————————————————————————————————————————————————————————————————————————
  41. // InstallDragHandlers attaches my tracking and receive handlers to
  42. // one of the application's windows
  43.  
  44. OSErr InstallDragHandlers(WindowPtr win)
  45. {
  46.     OSErr err;
  47.     
  48.     // install our tracking and receive handlers for the window    
  49.     err = InstallTrackingHandler(MyTrackingHandler, win, NULL);
  50.  
  51.     if (err == noErr) {
  52.         err = InstallReceiveHandler(MyReceiveHandler, win, NULL);
  53.         if (err != noErr)
  54.             (void) RemoveTrackingHandler(MyTrackingHandler, win);
  55.     }
  56.     
  57.     return err;
  58. }
  59.  
  60.  
  61. // —————————————————————————————————————————————————————————————————————————
  62. // —————————————————————————————————————————————————————————————————————————
  63. // RemoveDragHandlers removes my tracking and receive handlers from
  64. // one of the application's windows (prior to the window's disposal,
  65. // presumably)
  66.  
  67. void RemoveDragHandlers(WindowPtr win)
  68. {
  69.     RemoveReceiveHandler(MyReceiveHandler, win);
  70.     RemoveTrackingHandler(MyTrackingHandler, win);
  71. }
  72.  
  73.  
  74. // —————————————————————————————————————————————————————————————————————————
  75. // —————————————————————————————————————————————————————————————————————————
  76. // MouseInContentRgn returns true if the current mouse is in the content
  77. // area of the window (but not necessarily in the visible rgn)
  78.  
  79. Boolean MouseIsInContentRgn(DragReference theDrag, WindowPtr win)
  80. {
  81.     Point mousePt;
  82.     
  83.     (void) GetDragMouse(theDrag, &mousePt, NULL);
  84.     return PtInRgn(mousePt, ((WindowPeek) win)->contRgn);
  85. }
  86.  
  87.  
  88. // —————————————————————————————————————————————————————————————————————————
  89. // —————————————————————————————————————————————————————————————————————————
  90. // DragItemsAreAcceptable returns true if the contents (data) of
  91. // the drag are acceptable by a window of this application
  92. //
  93. // DragItemsAreAcceptable is called by the tracking and 
  94. // receive handlers
  95.  
  96. Boolean DragItemsAreAcceptable(DragReference theDrag)
  97. {
  98.     OSErr            err;
  99.     unsigned short    totalItems;
  100.     ItemReference    itemRef;
  101.     Boolean            acceptableFlag;
  102.     HFSFlavor         currHFSFlavor;
  103.     Size            flavorDataSize;
  104.     
  105.     acceptableFlag = false;
  106.  
  107.     // this app can only accept the drag of a single item
  108.     err = CountDragItems(theDrag, &totalItems);
  109.     if (err == noErr && totalItems == 1) {
  110.     
  111.         // get the reference number of the dragged item
  112.         err = GetDragItemReferenceNumber(theDrag, 1, &itemRef);
  113.  
  114.         if (err == noErr) {            
  115.             flavorDataSize = sizeof(HFSFlavor);
  116.             err = GetFlavorData(theDrag, itemRef, flavorTypeHFS, &currHFSFlavor,
  117.                 &flavorDataSize, 0);
  118.             
  119.             if (err == noErr) 
  120.                 acceptableFlag = true;
  121.         }
  122.     }
  123.     return acceptableFlag;
  124. }
  125.  
  126.  
  127. // —————————————————————————————————————————————————————————————————————————
  128. // —————————————————————————————————————————————————————————————————————————
  129. // DragIsNotInSourceWindow returns true if the drag in progress
  130. // is not in the same window it originated in
  131. //
  132. // DragIsNotInSourceWindow is called by the tracking and receive handlers
  133. //
  134. // Note that, if this application allowed items to be dragged within
  135. // its windows, this function would not be appropriate.
  136. // Instead, hilighting would probably occur in the source window
  137. // when the dragHasLeftSourceWindow flag is set, and the receive
  138. // handler wouldn't check this at all
  139.  
  140. Boolean DragIsNotInSourceWindow(DragReference theDrag)
  141. {
  142.     DragAttributes currDragFlags;
  143.     
  144.     (void) GetDragAttributes(theDrag, &currDragFlags);
  145.     return ((currDragFlags & dragInsideSenderWindow) == 0);
  146. }
  147.  
  148.  
  149. // —————————————————————————————————————————————————————————————————————————
  150. // —————————————————————————————————————————————————————————————————————————
  151. // MyTrackingHandler is called by the drag manager whenever a drag is
  152. // over one of the application's windows
  153. //
  154. // upon entry, the current port has been set to win by the Drag Manager
  155.  
  156. pascal OSErr MyTrackingHandler(DragTrackingMessage theMessage, WindowPtr win,
  157.     void *handlerRefCon, DragReference theDrag)
  158. {
  159. #pragma unused (handlerRefCon)
  160.  
  161.     RgnHandle    tempRgn;
  162.     short        mouseInWhichSpace;
  163.     OSErr        err;
  164.     
  165.     err = noErr;
  166.     
  167.     switch (theMessage) {
  168.     
  169.         case dragTrackingEnterHandler:            
  170.             //
  171.             // Any initialization for this window handler.
  172.             //
  173.             gDragHandlerGlobals.acceptableDragFlag = 
  174.                 DragItemsAreAcceptable(theDrag);
  175.             gDragHandlerGlobals.hilitedSpace = -1;
  176.             
  177.             // let the drag manager know if we can't accept this drag
  178.             if (!gDragHandlerGlobals.acceptableDragFlag)
  179.                 err = dragNotAcceptedErr;
  180.             break;
  181.             
  182.         case dragTrackingEnterWindow: 
  183.         case dragTrackingInWindow:
  184.         case dragTrackingLeaveWindow:            
  185.             //
  186.             // Highlighting of the window during a drag is done
  187.             // here.  Do it only if we can accept these items
  188.             // and we're not in the source window...
  189.             //        
  190.             if (gDragHandlerGlobals.acceptableDragFlag
  191.                     && DragIsNotInSourceWindow(theDrag)) {        
  192.                 //
  193.                 // Unless the mouse is leaving the visible area of the
  194.                 // window, check if it's in the window's content region
  195.                 //
  196.                 if (theMessage == dragTrackingLeaveWindow)
  197.                     mouseInWhichSpace = kNoSpace;
  198.  
  199.                 else {
  200.                     Point localPt;
  201.                 
  202.                     (void) GetDragMouse(theDrag, &localPt, 0L);
  203.                     GlobalToLocal(&localPt);
  204.                     mouseInWhichSpace = WhichSpace(win, localPt);
  205.                 }
  206.  
  207.                 //
  208.                 // If the mouse's space is not equal to the hilitedSpace
  209.                 // and the mouse is in a space  (i.e., a new, different space.)
  210.                 //
  211.                 if ((mouseInWhichSpace != gDragHandlerGlobals.hilitedSpace)
  212.                     && (mouseInWhichSpace != kNoSpace)) {
  213.                     Rect    nuSpaceRect, oldSpaceRect;
  214.  
  215.                     if (gDragHandlerGlobals.hilitedSpace != kNoSpace) {        
  216.                         //
  217.                         // There is a currently hilited space, unhilite it
  218.                         //
  219.                         GetSpaceRect(win, gDragHandlerGlobals.hilitedSpace, &oldSpaceRect);
  220.                         (void) HideDragHilite(theDrag);
  221.                     }
  222.  
  223.                     GetSpaceRect(win, mouseInWhichSpace, &nuSpaceRect);
  224.  
  225.                     // For cosmetic purposes
  226.                     InsetRect(&nuSpaceRect, 1, 1);        
  227.                     tempRgn = NewRgn();
  228.                     RectRgn(tempRgn, &nuSpaceRect);
  229.  
  230.                     // draw the hilight
  231.                     if (ShowDragHilite(theDrag, tempRgn, true) == noErr)
  232.                         // remember where hilighting is on
  233.                         gDragHandlerGlobals.hilitedSpace = mouseInWhichSpace;                    
  234.                     
  235.                     DisposeRgn(tempRgn);
  236.                 }
  237.                 
  238.                 //
  239.                 // else if the mouse is not in the content region
  240.                 // and the window is hilighted, erase the hilight
  241.                 //
  242.                 else if ((mouseInWhichSpace == kNoSpace) 
  243.                             && (gDragHandlerGlobals.hilitedSpace != kNoSpace)) {
  244.                     Rect oldSpaceRect;
  245.                     
  246.                     GetSpaceRect(win, gDragHandlerGlobals.hilitedSpace, &oldSpaceRect);                    
  247.                     
  248.                     if (HideDragHilite(theDrag) == noErr)
  249.                         // remember that nothing is hilited
  250.                         gDragHandlerGlobals.hilitedSpace = kNoSpace;
  251.                 }
  252.             }
  253.             break;
  254.  
  255.         // do nothing for the leaveHandler message
  256.         case dragTrackingLeaveHandler:
  257.             break;
  258.         
  259.         // let the drag manager know if we didn't recognize the message
  260.         default:
  261.             err = paramErr;
  262.     }
  263.     
  264.     return err;
  265. }
  266.  
  267.  
  268. //----------------------------------------------------------------------------
  269. // CreateAETarget
  270. //----------------------------------------------------------------------------
  271. #pragma segment AESupport
  272. OSErr CreateAETarget(ProcessSerialNumber *targetPSN, AEDesc *targetDesc)
  273. {
  274.     OSErr    err;
  275.  
  276.     err = AECreateDesc(typeProcessSerialNumber, (Ptr) targetPSN,
  277.         sizeof(ProcessSerialNumber), targetDesc);
  278.  
  279.     return err;
  280. }
  281.  
  282.  
  283. // —————————————————————————————————————————————————————————————————————————
  284. // —————————————————————————————————————————————————————————————————————————
  285. // MyReceiveHandler is called by the drag manager whenever a drag is
  286. // ends on one of the application's windows
  287.  
  288. pascal OSErr MyReceiveHandler(WindowPtr win, void *handlerRefCon, 
  289.         DragReference theDrag)
  290. {
  291. #pragma unused (handlerRefCon)
  292.     ItemReference    itemRef;
  293.     Size                dataSize;
  294.     HFSFlavor        theHFSFlavor;
  295.     Boolean            dataObtainedFlag;
  296.     OSErr                err;
  297.     
  298.     dataObtainedFlag = false;
  299.     if (!DragItemsAreAcceptable(theDrag) ||
  300.             !MouseIsInContentRgn(theDrag, win) ||
  301.             !DragIsNotInSourceWindow(theDrag)) 
  302.         return dragNotAcceptedErr;
  303.  
  304.     // There is only one item, so get its reference number.
  305.     err = GetDragItemReferenceNumber(theDrag, 1, &itemRef);
  306.     if (err != noErr)
  307.         return err;
  308.     
  309.     if (!dataObtainedFlag) {
  310.         dataSize = sizeof(HFSFlavor);
  311.         err = GetFlavorData(theDrag, itemRef, flavorTypeHFS, 
  312.                                                 &theHFSFlavor, &dataSize, 0);
  313.         if (err == noErr)
  314.             SendWindowSpaceSetter(win, theDrag, &theHFSFlavor.fileSpec);
  315.     }
  316.  
  317.     InvalRect(&win->portRect);
  318.  
  319.     return err;
  320. }
  321.  
  322.  
  323. // —————————————————————————————————————————————————————————————————————————
  324. // —————————————————————————————————————————————————————————————————————————
  325. // OutlineRegion changes a region into a tracing of its border
  326. // which is appropriate for normal dragging
  327. //
  328. // OutlineRegion is called by DoWindowContentDrag
  329.  
  330. void OutlineRegion(RgnHandle theRgn)
  331. {
  332.     RgnHandle tempRgn;
  333.     
  334.     tempRgn = NewRgn();
  335.     CopyRgn(theRgn, tempRgn);
  336.     InsetRgn(tempRgn, 1, 1);
  337.     DiffRgn(theRgn, tempRgn, theRgn);
  338.     DisposeRgn(tempRgn);
  339. }
  340.  
  341.  
  342. // —————————————————————————————————————————————————————————————————————————
  343. // —————————————————————————————————————————————————————————————————————————
  344. // In our send data proc, we need to create a dummy file and call SetDrag-
  345. // ItemFlavorData with it to trick the Finder into believing that we did
  346. // what it asked.  So now if the call to TrackDrag succeeds, we'll delete
  347. // this file and move the file via some Finder events.
  348. // 
  349. pascal OSErr MySendDataProc(FlavorType flavorType, void *refCon,
  350.                                ItemReference theItem, DragReference theDrag)
  351. {
  352.     Str63        fileName;
  353.     long        dirID;
  354.     short        vRefNum;
  355.     OSErr        result ;
  356.     FSSpec        locationSpec, dirSpec;
  357.     Boolean        isDir;
  358.  
  359.     if (flavorType == flavorTypeBlankSpec) {    
  360.         //
  361.         // Attempt to create a unique file name
  362.         //
  363.         NumToString(TickCount() + theItem, fileName);
  364.     
  365.         result = GetDropFSSpec(theDrag, &dirSpec);
  366.         if (result == noErr) {
  367.             //
  368.             // Get the directory ID and volume reference number from the drop location 
  369.             //
  370.             vRefNum = dirSpec.vRefNum;
  371.             result = DirIDFromFSSpec(&dirSpec, &dirID, &isDir);
  372.             
  373.             if (result == noErr) {
  374.                 result = FSMakeFSSpec(vRefNum, dirID, fileName, &locationSpec);
  375.     
  376.                 if (result == fnfErr) {        
  377.                     result = FSpCreate(&locationSpec, 'ttxt', 'TEXT', smSystemScript);
  378.             
  379.                     if (result == noErr) {
  380.                         result = SetDragItemFlavorData(theDrag, theItem, flavorType,
  381.                                                         &locationSpec, sizeof(FSSpec), 0L);
  382.                         (void) FSpSetIsInvisible(&locationSpec);                
  383.                         FailOSErr(result);
  384.                     }
  385.                 }
  386.                 else
  387.                     result = dupFNErr;    /* force an error */
  388.             }
  389.         }
  390.     }    
  391.     else
  392.         DebugStr("\p it wants something other than blankSpec");
  393.         
  394.         
  395.     if (result == noErr)
  396.         return result;
  397.     else {
  398.         Debugger();
  399.         return cantGetFlavorErr;
  400.     }
  401. }
  402.  
  403.  
  404. // —————————————————————————————————————————————————————————————————————————
  405. // —————————————————————————————————————————————————————————————————————————
  406.  
  407. OSErr AddPromiseHFSFlavor(DragReference drag, ItemReference item, FSSpecPtr spec)
  408. {
  409.     PromiseHFSFlavor    phfsObj;
  410.     OSErr                err;
  411.     FInfo                fileInfo;
  412.     
  413.     err = FSpGetFInfo(spec, &fileInfo);
  414.     if (err == noErr) {
  415.         phfsObj.fileType = fileInfo.fdType;
  416.         phfsObj.fileCreator = fileInfo.fdCreator;
  417.         phfsObj.fdFlags = fileInfo.fdFlags;
  418.     
  419.     }
  420.  
  421.     phfsObj.promisedFlavor = flavorTypeBlankSpec;
  422.  
  423.     if (err != noErr) 
  424.         return err;
  425.  
  426.     //
  427.     // Goofy bug in the Drag Mgr.  If you don't add the promiseHFS
  428.     // flavor first, the drag will complete as expected, but the drop
  429.     // will be put at the local coordinates of the initial drag.
  430.     //
  431.     err = AddDragItemFlavor(drag, item, flavorTypePromiseHFS, 
  432.         (Ptr) &phfsObj, sizeof(PromiseHFSFlavor), 0);
  433.  
  434.     err = AddDragItemFlavor(drag, item, flavorTypeBlankSpec, 
  435.         NULL, 0, 0);
  436.  
  437.     err = AddDragItemFlavor(drag, kRealSpecItemRef, flavorTypeRealSpec, 
  438.             (Ptr) spec, sizeof(FSSpec), 0);
  439.  
  440.     return err;
  441. }
  442.  
  443.  
  444. // —————————————————————————————————————————————————————————————————————————
  445. // —————————————————————————————————————————————————————————————————————————
  446. // GetDragDummyFile
  447. //
  448.  
  449. OSErr GetDragDummyFile(DragReference theDrag, FSSpecPtr theSpec)
  450. {
  451.     OSErr            err;
  452.     unsigned short    totalItems;
  453.     ItemReference    itemRef;
  454.     Boolean            foundIt;
  455.     Size            flavorDataSize;
  456.     
  457.     foundIt = false;
  458.  
  459.     // this app can only accept the drag of a single item
  460.     err = CountDragItems(theDrag, &totalItems);
  461.  
  462.     if ((err == noErr) && (foundIt == false)) {
  463.         // get the reference number of the dragged item
  464.         err = GetDragItemReferenceNumber(theDrag, 1, &itemRef);
  465.  
  466.         if (err == noErr) {            
  467.             // use GetFlavorFlags to check on flavor existence of PICT data
  468.             // without forcing translation
  469.             
  470.             flavorDataSize = sizeof(FSSpec);
  471.             err = GetFlavorData(theDrag, itemRef, flavorTypeBlankSpec, theSpec,
  472.                 &flavorDataSize, 0);
  473.             
  474.             if (err == noErr)
  475.                 foundIt = true;
  476.         }
  477.     }
  478.  
  479.     return err;
  480. }
  481.  
  482.  
  483. //----------------------------------------------------------------------------
  484. //----------------------------------------------------------------------------
  485. // GetDragRegion
  486.  
  487. OSErr GetDragRegion(WindowPtr win, RgnHandle dragRegion, short spaceNum)
  488. {
  489.     Rect            tempRect;
  490.     OSErr            err;
  491.     Point            offsetPt = {0,0};
  492.     RgnHandle        insetRegion = NewRgn();
  493.     Handle            iconSuiteToUse, spacesIconSuite;
  494.  
  495.     GetSpaceRect(win, spaceNum, &tempRect);        
  496.     InsetRect(&tempRect, 9, 9);
  497.     GetSpaceIconSuite(win, spaceNum, &spacesIconSuite);
  498.     SetEmptyRgn(insetRegion);
  499.  
  500.     if (spacesIconSuite != NULL)
  501.         iconSuiteToUse = spacesIconSuite;
  502.  
  503.     err = IconSuiteToRgn(dragRegion, &tempRect, atNone, iconSuiteToUse);
  504.  
  505.     LocalToGlobal(&offsetPt);
  506.     OffsetRgn(dragRegion, offsetPt.h, offsetPt.v);
  507.     CopyRgn(dragRegion, insetRegion);
  508.     InsetRgn(insetRegion, 1,1);
  509.     DiffRgn(dragRegion, insetRegion, dragRegion);
  510.  
  511.     if ((iconSuiteToUse != spacesIconSuite) && (iconSuiteToUse != NULL))
  512.         // Use this to get rid of the iconSuite if it's generic
  513.         err = DisposeIconSuite(iconSuiteToUse, true);
  514.     
  515. GISuiteFailed:
  516.     return err;
  517. }
  518.  
  519.  
  520. // —————————————————————————————————————————————————————————————————————————
  521. // —————————————————————————————————————————————————————————————————————————
  522. // DoWindowContentDrag is called by the application whenever a drag
  523. // begins on one of the app's windows
  524.  
  525. OSErr DoWindowContentDrag(WindowPtr win, EventRecord *theEvent)
  526. {
  527.     OSErr            err;
  528.     DragReference    theDrag;
  529.     RgnHandle        dragRgn;
  530.     ItemReference    theItem = 1;
  531.     FSSpec            windowSpec;
  532.     short            mouseUpModifiers;
  533.     DragAttributes    currDragFlags;
  534.     GrafPtr            oldPort;
  535.     Point            localPt;
  536.     short            dragSpace;
  537.  
  538.     // create a new drag
  539.     err = NewDrag(&theDrag);
  540.     if (err != noErr) goto Bail;
  541.  
  542.     GetPort(&oldPort);
  543.     SetPort(win);
  544.     localPt = theEvent->where;
  545.     GlobalToLocal(&localPt);
  546.     SetPort(oldPort);
  547.     dragSpace = WhichSpace(win, localPt);
  548.     
  549.     if (dragSpace == kNoSpace) goto DisposeDragAndBail;
  550.     
  551.     err = GetSpaceSpec(win, dragSpace, &windowSpec);
  552.     if (err != noErr) goto DisposeDragAndBail;
  553.     
  554.     err = AddPromiseHFSFlavor(theDrag, theItem, &windowSpec);
  555.     if (err != noErr) goto DisposeDragAndBail;
  556.  
  557.     err = SetDragSendProc (theDrag, MySendDataProc, NULL);
  558.     if (err != noErr) goto DisposeDragAndBail;
  559.     
  560.     dragRgn = NewRgn();
  561.     err = GetDragRegion(win, dragRgn, dragSpace);
  562.     
  563.     // do the drag and clean up
  564.     err = TrackDrag(theDrag, theEvent, dragRgn);
  565.     
  566.     DisposeRgn(dragRgn);
  567.     
  568.     if (err == noErr) {
  569.         FSSpec    dummyFile;
  570.         
  571.         //
  572.         // the drag was successful, so delete the dummy file
  573.         // we created and move the file with Apple events.
  574.         //
  575.         err = GetDragDummyFile(theDrag, &dummyFile);
  576.         
  577.         if (err == noErr) {
  578.             err = FSpDelete(&dummyFile);
  579.             
  580.             if (err == noErr)
  581.                 err = HandleDragCloneMove(theDrag);
  582.             
  583.             FailOSErr(err);
  584.         }
  585.         
  586.         // if the option key was not pressed at mouseUp and the drag ended
  587.         // in the source application then this is a move operation and we
  588.         // have to clear the source window
  589.         
  590.         (void) GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers);        
  591.         (void) GetDragAttributes(theDrag, &currDragFlags);
  592.     
  593.     }
  594.     else if (err != userCanceledErr)
  595.         DebugStr("\pdrag failed");
  596.     
  597. DisposeDragAndBail:
  598.     DisposeDrag(theDrag);
  599.     
  600. Bail:
  601.     return err;
  602. }
  603.  
  604.  
  605.  
  606. // —————————————————————————————————————————————————————————————————————————
  607. // —————————————————————————————————————————————————————————————————————————
  608.  
  609. pascal OSErr DoAEOpenApplication(AppleEvent * theAppleEvent,
  610.                                  AppleEvent * replyAppleEvent, 
  611.                                  long refCon)
  612. {
  613. #pragma unused (theAppleEvent, replyAppleEvent, refCon)
  614.  
  615.     // make an empty window
  616.     (void) DoNewWindow();
  617.     return noErr;
  618. }
  619.  
  620. // —————————————————————————————————————————————————————————————————————————
  621. // —————————————————————————————————————————————————————————————————————————
  622.  
  623. pascal OSErr DoAEOpenDocuments(AppleEvent * theAppleEvent,
  624.                                AppleEvent * replyAppleEvent, 
  625.                                long refCon)
  626. {
  627. #pragma unused (replyAppleEvent, refCon)
  628.     OSErr        err;
  629.     FSSpec        currSpec;
  630.     AEDescList    docDescList;
  631.     long        itemCount, index;
  632.     AEKeyword    keyword;
  633.     DescType    typeCode;
  634.     Size        actualSize;
  635.     
  636.     // retrieve all documents from the Apple event and open them
  637.     err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList,
  638.         &docDescList);
  639.     if (err != noErr) goto Bail;
  640.     
  641.     err = AECountItems(&docDescList, &itemCount);
  642.     if (err != noErr) goto DisposeDocDescListAndBail;
  643.  
  644.      for (index = 1; index <= itemCount; index++) {
  645.          err = AEGetNthPtr(&docDescList, index, typeFSS,
  646.              &keyword, &typeCode, (Ptr) &currSpec, sizeof(FSSpec),
  647.              &actualSize);
  648.          if (err != noErr) goto DisposeDocDescListAndBail;
  649.          
  650.      }
  651.      
  652.  DisposeDocDescListAndBail:
  653.      (void) AEDisposeDesc(&docDescList);
  654.      
  655.  Bail:
  656.      if (err == noErr) return noErr;
  657.      else return errAEEventNotHandled;
  658. }
  659.  
  660. // —————————————————————————————————————————————————————————————————————————
  661. // —————————————————————————————————————————————————————————————————————————
  662.  
  663. pascal OSErr DoAEQuitApplication(AppleEvent * theAppleEvent,
  664.                                  AppleEvent * replyAppleEvent, 
  665.                                  long refCon)
  666. {
  667. #pragma unused (theAppleEvent, replyAppleEvent, refCon)
  668.  
  669.     DoQuit();     // set the quit flag (doesn't immediately quit)
  670.     return noErr;
  671. }
  672.  
  673.  
  674. // —————————————————————————————————————————————————————————————————————————
  675. // —————————————————————————————————————————————————————————————————————————
  676. // DoHighLevelEvent
  677.  
  678. void DoHighLevelEvent(EventRecord * theEventRecPtr)
  679. {
  680.     (void) AEProcessAppleEvent(theEventRecPtr);
  681. }
  682.  
  683.  
  684. // —————————————————————————————————————————————————————————————————————————
  685. // —————————————————————————————————————————————————————————————————————————
  686. // InstallAppleEventHandlers
  687.  
  688. OSErr InstallAppleEventHandlers()
  689. {
  690.     OSErr err;
  691.     
  692.     err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
  693.                 (AEEventHandlerProcPtr) DoAEOpenApplication, 0, false);
  694.                                     
  695.     if (err == noErr)
  696.         err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
  697.                 (AEEventHandlerProcPtr) DoAEOpenDocuments, 0, false);
  698.  
  699.     if (err == noErr)
  700.         err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
  701.                 (AEEventHandlerProcPtr) DoAEQuitApplication, 0, false);
  702.     
  703.     if (err == noErr)
  704.         err = AEInstallEventHandler(kPrivateEventClass, kAESetSpace,
  705.                 (AEEventHandlerProcPtr) DoAESetSpace, 0, false);
  706.     
  707.     return err;
  708. }
  709.  
  710.  
  711. // —————————————————————————————————————————————————————————————————————————
  712. // —————————————————————————————————————————————————————————————————————————
  713. // ConcatPascalStrings
  714.  
  715. void ConcatPascalStrings(StringPtr s1, StringPtr s2)
  716. {
  717.     short s1Length, s2Length;
  718.     
  719.     s1Length = s1[0];
  720.     s2Length = s2[0];
  721.     BlockMove(&s2[1], &s1[s1Length+1], s2Length);
  722.     s1[0] = s1Length + s2Length;
  723. }
  724.  
  725.  
  726. // —————————————————————————————————————————————————————————————————————————
  727. // —————————————————————————————————————————————————————————————————————————
  728. // GetApplicationName uses the Process Manager to find
  729. // the current app's name
  730.  
  731. OSErr GetApplicationName(StringPtr appNameString)
  732. {
  733.     ProcessInfoRec        appPIR;
  734.     ProcessSerialNumber    appPSN;
  735.     
  736.     appPSN.lowLongOfPSN = kCurrentProcess;
  737.     appPSN.highLongOfPSN = 0;
  738.     
  739.     appPIR.processInfoLength = sizeof(ProcessInfoRec);
  740.     appPIR.processAppSpec = NULL;
  741.     appPIR.processName = appNameString;
  742.     
  743.     return GetProcessInformation(&appPSN, &appPIR);
  744. }
  745.  
  746.  
  747.  
  748. // —————————————————————————————————————————————————————————————————————————
  749. // —————————————————————————————————————————————————————————————————————————
  750.  
  751. void BuildSpecString(const FSSpec *spec,
  752.                                    Str255 fullPathname)
  753. {
  754.     OSErr        result;
  755.     CInfoPBRec    pb;
  756.     Str255        nodeName;
  757.     
  758.     fullPathname[0] = 0;
  759.     result = noErr;
  760.  
  761.     /* Build a full pathname down to the volume */
  762.     pb.dirInfo.ioNamePtr = nodeName;
  763.     pb.dirInfo.ioVRefNum = spec->vRefNum;
  764.     pb.dirInfo.ioDrParID = spec->parID;
  765.     do {
  766.         pb.dirInfo.ioFDirIndex = -1;
  767.         pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
  768.         result = PBGetCatInfoSync(&pb);
  769.         concat(fullPathname, 3, nodeName, "\p:", fullPathname);
  770.     } while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) );
  771.     
  772.     /* add the spec's name field and we're done */
  773.     pstrcat(fullPathname, (StringPtr)spec->name);
  774. }
  775.  
  776.  
  777.  
  778. // —————————————————————————————————————————————————————————————————————————
  779. // —————————————————————————————————————————————————————————————————————————
  780. // DrawWindow draws a window's picture in the window
  781.  
  782. void DrawWindow(WindowPtr win)
  783. {
  784.     Str255            str;
  785.     FSSpec            spec;
  786.     WindowDataHandle    winData;
  787.     OSErr            err;
  788.  
  789.     winData = (WindowDataHandle) GetWRefCon(win);
  790.     if (winData != NULL) {
  791.         Rect    rct;
  792.         short    count;
  793.         Handle    iconSuite;
  794.  
  795.         for (count = 0; count < kNumberOfSpaces; count ++) {
  796.             GetSpaceRect(win, count, &rct);
  797.             FrameRect(&rct);
  798.             err = GetSpaceSpec(win, count, &spec);
  799.  
  800.             if (err == noErr) {
  801.                 BuildSpecString(&spec, str);
  802.                 MoveTo(rct.right + 15, (rct.top + rct.bottom) / 2);
  803.                 DrawString(str);
  804.             }
  805.             
  806.             GetSpaceIconSuite(win, count, &iconSuite);
  807.         
  808.             if (iconSuite != NULL) {
  809.                 InsetRect(&rct, 9, 9);
  810.                 err = PlotIconSuite(&rct, atNone, ttNone, iconSuite);
  811.             }
  812.         }
  813.     }
  814. }
  815.  
  816.  
  817. // ReportStringInWindow draws a string in the specified window
  818. // if win is NULL, a new window is created and its WindowPtr is returned
  819.  
  820. WindowPtr ReportStringInWindow(WindowPtr win, StringPtr theString)
  821. {
  822.     DebugStr(theString);
  823.     return win;
  824. }
  825.  
  826.  
  827. // —————————————————————————————————————————————————————————————————————————
  828. // —————————————————————————————————————————————————————————————————————————
  829. // CreateMenus makes menus the old-fashioned way
  830.  
  831. void CreateMenus()
  832. {
  833.     // create Apple menu
  834.     gAppleMenuHandle = NewMenu(kAppleMenuID, "\p\024");
  835.     AppendMenu(gAppleMenuHandle, "\pAbout FinderDrag...;(-");
  836.     AddResMenu(gAppleMenuHandle, 'DRVR');
  837.     InsertMenu(gAppleMenuHandle, 0);
  838.  
  839.     // create File menu
  840.     gFileMenuHandle = NewMenu(kFileMenuID, "\pFile");
  841.     AppendMenu(gFileMenuHandle, "\pNew/N;Open.../O;Close/W;(-;Quit/Q");
  842.     InsertMenu(gFileMenuHandle, 0);
  843.     
  844.     DrawMenuBar();
  845. }
  846.  
  847.  
  848. // —————————————————————————————————————————————————————————————————————————
  849. // —————————————————————————————————————————————————————————————————————————
  850. // DoNewWindow creates a new window with an empty picture
  851. // and returns the window's WindowPtr
  852.  
  853. WindowPtr DoNewWindow()
  854. {
  855.     WindowPtr            newWindow;
  856.     Str63                windowNameStr;
  857.     Str15                numStr;
  858.     WindowDataHandle    newWindowDataHandle;
  859.     Rect                windowRect;
  860.     
  861.     // one more window in the world
  862.     gWindowCounter++;
  863.     
  864.     // find a size and place for the new window
  865.     windowRect = qd.screenBits.bounds;
  866.     SetRect(&windowRect, 60, 60, 360, 205);
  867.     OffsetRect(&windowRect, 20 * (gWindowCounter % 10), 20 * (gWindowCounter % 10));
  868.  
  869.     // make a name for the window by concatenating gWindowCounter to the app name
  870.     NumToString(gWindowCounter, numStr);
  871.     if (GetApplicationName(windowNameStr) != noErr)
  872.         *windowNameStr = 0;
  873.     ConcatPascalStrings(windowNameStr, "\p ");
  874.     ConcatPascalStrings(windowNameStr, numStr);
  875.     
  876.     // open the window
  877.     if (gHasColorQuickdrawFlag)
  878.         newWindow = NewCWindow(NULL, &windowRect, windowNameStr, true,
  879.             documentProc, (WindowPtr) -1, true, 0);
  880.     else
  881.         newWindow = NewWindow(NULL, &windowRect, windowNameStr, true,
  882.             documentProc, (WindowPtr) -1, true, 0);
  883.     
  884.     if (newWindow == NULL) goto Bail;
  885.     
  886.     SetPort(newWindow);
  887.     ClipRect(&newWindow->portRect);
  888.     TextSize(10);
  889.  
  890.     // attach my drag handlers to this window
  891.     
  892.     if (InstallDragHandlers(newWindow) != noErr)
  893.         goto DisposeWindowAndBail;
  894.     
  895.     // allocate a data structure and a blank picture for the window
  896.     
  897.     newWindowDataHandle = (WindowDataHandle) NewHandle(sizeof(WindowData));
  898.     if (newWindowDataHandle == NULL) goto RemoveHandlersAndBail;
  899.  
  900.     InitSpaces(newWindowDataHandle);
  901.  
  902.     SetWRefCon(newWindow, (long) newWindowDataHandle);
  903.         
  904.     return newWindow;
  905.  
  906. RemoveHandlersAndBail:
  907.     RemoveDragHandlers(newWindow);
  908.     
  909. DisposeWindowAndBail:
  910.     DisposeWindow(newWindow);
  911.     
  912. Bail:
  913.     gWindowCounter--;
  914.     return NULL;
  915.     
  916. }
  917.  
  918. // —————————————————————————————————————————————————————————————————————————
  919. // —————————————————————————————————————————————————————————————————————————
  920. // DoCloseWindow disposes of a window and does all necessary clean-up
  921.  
  922. void DoCloseWindow(WindowPtr win)
  923. {
  924.     if (win != NULL) {
  925.         DisposeHandle((Handle) GetWRefCon(win));
  926.         RemoveDragHandlers(win);
  927.         DisposeWindow(win);
  928.     }
  929. }
  930.  
  931. // —————————————————————————————————————————————————————————————————————————
  932. // —————————————————————————————————————————————————————————————————————————
  933. // DoQuit closes all open windows and sets the global quit flag
  934.  
  935. void DoQuit()
  936. {
  937.     while (FrontWindow() != NULL)
  938.         DoCloseWindow(FrontWindow());
  939.     gQuitFlag = true;
  940. }
  941.  
  942.  
  943. // —————————————————————————————————————————————————————————————————————————
  944. // —————————————————————————————————————————————————————————————————————————
  945. // DoAboutWindow
  946.  
  947. void DoAboutWindow()
  948. {
  949.     Alert(128, NULL);
  950. }
  951.  
  952.  
  953. // —————————————————————————————————————————————————————————————————————————
  954. // —————————————————————————————————————————————————————————————————————————
  955. // DoOpen
  956.  
  957. void DoOpen()
  958. {
  959.     StandardFileReply reply;
  960.     
  961.     StandardGetFile(NULL, -1, NULL, &reply);
  962.     if (reply.sfGood) {
  963.  
  964.     //    err = GetIconSuiteFromFinder(&reply.sfFile, &iconSuite);
  965.         SetSpaceIndex(FrontWindow(), 1, &reply.sfFile);
  966.  
  967.     }
  968.     else
  969.         SysBeep(20);
  970.  
  971. }
  972.  
  973.  
  974.  
  975. // —————————————————————————————————————————————————————————————————————————
  976. // —————————————————————————————————————————————————————————————————————————
  977. // DoMenuCommand handles user menu selections
  978.  
  979. void DoMenuCommand(long menuVal)
  980. {
  981.     short        theItem, theMenu;
  982.     Str255        deskAccessoryName;
  983.     
  984.     theItem = LoWord(menuVal);
  985.     theMenu = HiWord(menuVal);
  986.  
  987.     switch (theMenu) {
  988.  
  989.         case kAppleMenuID:
  990.             if (theItem == kAboutMenuItem)
  991.                 DoAboutWindow();
  992.                 
  993.             else {
  994.                 GetItem(gAppleMenuHandle, theItem, deskAccessoryName);
  995.                 (void) OpenDeskAcc(deskAccessoryName);
  996.             }
  997.             break;
  998.  
  999.         case kFileMenuID:
  1000.  
  1001.             if (theItem == kNewMenuItem)
  1002.                 (void) DoNewWindow();
  1003.  
  1004.             else if (theItem == kOpenMenuItem)
  1005.                 DoOpen();
  1006.             
  1007.             else if (theItem == kCloseMenuItem)
  1008.                 DoCloseWindow(FrontWindow());
  1009.                 
  1010.             else if (theItem == kQuitMenuItem)
  1011.                 DoQuit();
  1012.                 
  1013.             break;
  1014.  
  1015.     }
  1016.     HiliteMenu(0);      // unhilight menu title
  1017. }
  1018.  
  1019.  
  1020. // —————————————————————————————————————————————————————————————————————————
  1021. // —————————————————————————————————————————————————————————————————————————
  1022.  
  1023. void main()
  1024. {
  1025.     OSErr        err;
  1026.     long        gestResponse;
  1027.     Boolean        canRunFlag;
  1028.     
  1029.     
  1030.     // initialize the toolbox
  1031.     InitGraf(&qd.thePort);
  1032.     InitFonts();
  1033.     InitWindows();
  1034.     InitMenus();
  1035.     TEInit();
  1036.     InitDialogs(NULL);
  1037.     InitCursor();
  1038.     FlushEvents(everyEvent,0);
  1039.     MaxApplZone();
  1040.     
  1041.     // can we run?  let's be optimistic
  1042.     canRunFlag = true;
  1043.     
  1044.     // are the Apple Event and Drag managers available?
  1045.     // they simply must be
  1046.     
  1047.     err = Gestalt(gestaltAppleEventsAttr, &gestResponse);
  1048.     if (err != noErr ||
  1049.         (gestResponse & (1 << gestaltAppleEventsPresent)) == 0)
  1050.         
  1051.         canRunFlag = false;
  1052.  
  1053.     err = Gestalt(gestaltDragMgrAttr, &gestResponse);
  1054.     if (err != noErr ||
  1055.         (gestResponse & (1 << gestaltDragMgrPresent)) == 0)
  1056.         
  1057.         canRunFlag = false;
  1058.  
  1059.     if (!canRunFlag) ExitToShell();  // an alert would be nicer
  1060.     
  1061.     
  1062.     // use Gestalt to find out what the world is like
  1063.     // in particular, check for Color QuickDraw
  1064.     
  1065.     err = Gestalt(gestaltQuickdrawVersion, &gestResponse);
  1066.     if (err != noErr || gestResponse < 0x0100)
  1067.         gHasColorQuickdrawFlag = false;
  1068.     else
  1069.         gHasColorQuickdrawFlag = true;
  1070.  
  1071.  
  1072.     // now let's initialize everything and install the
  1073.     // drag and Apple event handlers
  1074.  
  1075.     // initialize application globals
  1076.     
  1077.     gQuitFlag = false;
  1078.     gWindowCounter = 0;
  1079.     
  1080.     // install Apple event handlers
  1081.     (void) InstallAppleEventHandlers();
  1082.     err = InitFinderAE();
  1083.     FailOSErr(err);
  1084.     
  1085.     // make the menus
  1086.     CreateMenus();
  1087.         
  1088.     // main event loop
  1089.     
  1090.     while (!gQuitFlag) {
  1091.  
  1092.         ProcessEvents();
  1093.  
  1094.     }
  1095.     
  1096.     // Good night
  1097. }
  1098.  
  1099.  
  1100. // —————————————————————————————————————————————————————————————————————————
  1101. // —————————————————————————————————————————————————————————————————————————
  1102.  
  1103. void ProcessEvents(void)
  1104. {
  1105.     EventRecord    mainEventRec;
  1106.     Boolean        eventFlag;
  1107.     
  1108.     WindowPtr    whichWindow;
  1109.     Rect        tempRect;
  1110.  
  1111.     eventFlag = WaitNextEvent(everyEvent, &mainEventRec, 60*60*60, NULL);
  1112.     
  1113.     switch(mainEventRec.what) {
  1114.     
  1115.         case mouseDown:
  1116.             switch (FindWindow(mainEventRec.where, &whichWindow)) {
  1117.  
  1118.                 case inSysWindow:  // desk accessory window
  1119.                     SystemClick(&mainEventRec, whichWindow);
  1120.                     break;
  1121.                     
  1122.                 case inMenuBar:
  1123.                     DoMenuCommand(MenuSelect(mainEventRec.where));
  1124.                     break;
  1125.                     
  1126.                 case inDrag:
  1127.                     tempRect = qd.screenBits.bounds;
  1128.                     DragWindow(whichWindow, mainEventRec.where, &tempRect);
  1129.                     break;
  1130.                     
  1131.                 case inGoAway:
  1132.                     if (TrackGoAway(whichWindow, mainEventRec.where))
  1133.                         DoCloseWindow(whichWindow);
  1134.                     break;
  1135.                     
  1136.                 case inContent:
  1137.                 
  1138.                     // check to see if the user is starting a drag
  1139.                     if (WaitMouseMoved(mainEventRec.where))
  1140.                         
  1141.                         // if so, do the drag magic
  1142.                         DoWindowContentDrag(whichWindow, &mainEventRec);
  1143.                         
  1144.                     else if (whichWindow != FrontWindow())
  1145.                         SelectWindow(whichWindow);
  1146.                     break;
  1147.             }
  1148.             break;
  1149.  
  1150.         case updateEvt:
  1151.         
  1152.             whichWindow = (WindowPtr) mainEventRec.message;
  1153.         
  1154.             if ( ((WindowPeek)whichWindow)->windowKind != dialogKind )
  1155.             {
  1156.                 SetPort(whichWindow);
  1157.                 BeginUpdate(whichWindow);
  1158.                 
  1159.                 EraseRect(&whichWindow->portRect);
  1160.                 DrawWindow(whichWindow);
  1161.                 
  1162.                 EndUpdate(whichWindow);
  1163.             }
  1164.             break;
  1165.                     
  1166.         case keyDown:
  1167.         case autoKey:
  1168.             if (mainEventRec.modifiers & cmdKey)
  1169.                 DoMenuCommand(MenuKey(mainEventRec.message & charCodeMask));
  1170.             break;
  1171.             
  1172.         case kHighLevelEvent:
  1173.             DoHighLevelEvent(&mainEventRec);
  1174.             break;
  1175.         
  1176.         // a real app should handle all of these events.
  1177.         // Since the code is in Inside Mac:Mac Toolbox Essentials,
  1178.         // you really have no excuse for not supporting all the
  1179.         // standard low-level events
  1180.         
  1181.         
  1182.         
  1183.         // however, this is just a sample program, so I have an excuse
  1184.         
  1185.         case activateEvt:
  1186.         case osEvt:
  1187.         case diskEvt:
  1188.         case mouseUp:
  1189.             break;
  1190.     }
  1191. }
  1192.  
  1193.